package cn.com.idmy.id.segment;

import cn.com.idmy.id.segment.bus.FillSegmentEvent;
import cn.com.idmy.id.segment.bus.FillSegmentUpdater;
import cn.com.idmy.id.segment.model.IdState;
import cn.com.idmy.id.segment.policy.FetchPolicy;
import cn.com.idmy.id.service.LeafService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

@Slf4j
public class SegmentBuffer {
    public static final int CHANGE_NEXT_BUFFER = 3;
    public static final int FILLED_NEXT_BUFFER = 2;
    public static final int FILLING_NEXT_BUFFER = 1;
    public static final int NORMAL = 0;
    private volatile int state = NORMAL;
    private volatile Segment curSegment;
    @Setter
    private volatile Segment nextSegment;
    private final LeafService leafService;
    private static final VarHandle STATE_HANDLE;
    private final FillSegmentUpdater fillSegmentUpdater;
    private final FetchPolicy fetchPolicy;
    private final String tag;
    private Throwable throwable;

    static {
        try {
            // 使用VarHandle替代Unsafe
            var lookup = MethodHandles.lookup();
            STATE_HANDLE = lookup.findVarHandle(SegmentBuffer.class, "state", int.class);
        } catch (Exception e) {
            throw new IllegalArgumentException(e.getMessage(), e);
        }
    }

    public SegmentBuffer(LeafService leafService, FillSegmentUpdater fillSegmentUpdater, FetchPolicy fetchPolicy, String tag) {
        this.leafService = leafService;
        this.fillSegmentUpdater = fillSegmentUpdater;
        this.fetchPolicy = fetchPolicy;
        this.tag = tag;
        curSegment = new Segment();
    }

    public IdState nextID(int num) {
        checkException();
        checkSegment();
        IdState idState;
        for (; ; ) {
            idState = curSegment.nextId(num);
            if (idState == null) {
                // wait until next segment ok
                checkSegment();
                while (true) {
                    checkException();
                    if (state == NORMAL) {
                        break;
                    }
                    // cas 配置
                    if (state == FILLED_NEXT_BUFFER && nextSegment != null && compareAndSwapState(FILLED_NEXT_BUFFER, CHANGE_NEXT_BUFFER)) {
                        log.info("{}, next segment ok", tag);
                        changeSegmentAndNotify();
                        break;
                    }

                    synchronized (this) {
                        try {
                            //等待填充
                            this.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } else {
                break;
            }
        }
        return idState;
    }

    public void fillComplete(Throwable e) {
        this.throwable = e;
        state = FILLED_NEXT_BUFFER;
        synchronized (this) {
            this.notify();
        }
    }

    /**
     * 检查填充是否发生异常
     */
    private void checkException() {
        if (throwable != null) {
            if (throwable instanceof IllegalArgumentException) {
                throw (IllegalArgumentException) throwable;
            } else {
                throw new IllegalArgumentException("检测到异常,tag{" + tag + "}", throwable);
            }
        }
    }

    /**
     * 交换段缓冲
     */
    private void changeSegmentAndNotify() {
        curSegment = nextSegment;
        nextSegment = null;
        state = NORMAL;
        synchronized (this) {
            this.notifyAll();
        }
    }

    /**
     * 检查是否达到切换填充下一个缓冲的比例
     */
    private void checkSegment() {
        // 达到填充阈值 且 下一个缓冲区为空 且 没有存在争用的状态下
        if (curSegment.percent().compareTo(fetchPolicy.nextSegmentFetchPercent(this.tag)) >= 0
                && nextSegment == null
                && compareAndSwapState(NORMAL, FILLING_NEXT_BUFFER)) {
            // 构建填充消息
            log.info("开始填充:{}", tag);
            FillSegmentEvent fillSegmentEvent = new FillSegmentEvent(this, leafService, fetchPolicy.segmentFetchSize(this.tag), tag);
            fillSegmentUpdater.notifyFill(fillSegmentEvent);
        }
    }

    private boolean compareAndSwapState(int curState, int newState) {
        // 使用VarHandle的CAS操作替代Unsafe.compareAndSwapInt
        return STATE_HANDLE.compareAndSet(this, curState, newState);
    }
}
